iT邦幫忙

2022 iThome 鐵人賽

DAY 13
1

前言

今天來討論比較進階的效能問題,我們俗稱的「long list」,也就是畫面上那種很長的 list 或 table,滾輪要一直滑才會到最底的那種。

程式方面要寫出來其實不難,因為很單純就是

  • 跟後端撈資料回來(通常是 array)
  • 把資料跑 .map() 產生 JSX 語法 HTML

所以不管資料是幾十筆、幾百筆,反正程式都一樣,都是跑 .map(),只是產出來之後才會發現,雖然畫面內容都正確,卻會有失幀、lag 等效能 issue

到了這時才會發現,寫對是一回事,寫好更是需要下功夫。

先想一下

  • Virtualized List 是在什麼樣的時代誕生的?
  • Virtualized List 怎麼解決問題?
  • Virtualized List 的優缺點是什麼?
  • Virtualized List 適合什麼情境?

Virtualized List 是在什麼樣的時代誕生的?

在面對 long list 的時候,其實一開始是沒什麼感覺的,畢竟剛開始沒什麼資料,畫面一下就 render 好了,就算改個 state 重新 render,也是一轉眼之間的事情。

但隨著使用者增加,資料漸增,效能方面也開始受到影響,不再是一轉眼的事情。

例如 FB、Twitter 等社群媒體會有大量的文章列表,如果有 1000 篇文章,就必須生成 1000 個 DOM 節點,每個節點底下還包含了圖片、影音等多媒體資料。

像這樣同時 render 數量龐大的元素會有幾個明顯的缺點:

  • 首次載入時間長:使用者看到白屏時間會比較長
  • 記憶體用量大:在 render 了大量 DOM 節點的狀況下,滾動時會大大增加記憶體的用量
  • 容易失幀:因為 render 很慢,所以無法維持瀏覽器的幀率,頁面會顯得卡頓,性能差的裝置更是明顯
  • 失去響應:使用者無法點擊、滑動,只能乾等

Virtualized List 怎麼解決問題?

React 官方建議要渲染 long list 時,可以使用 Virtualized list 技術來最佳化效能,是優化 long list 的一種技巧:

儲存所有列表元素的位置,只渲染可視區(viewport)內的列表元素,當可視區滾動時,根據滾動的 offset 大小以及所有列表元素的位置,計算在可視區應該渲染哪些元素,這種技術也叫做「Windowing

Windowing 背後原理示意

背後原理

正常的 element 排列方式是 static,每個 element 會按照順序「佔位子」,因此每個 element 是互相影響關聯的(上面的元素長愈高,下面的元素就愈被往下擠)

但為了做到 windowing 的效果,「不是所有 element 都需要 render 在畫面上」,因此需要打破這個關聯,改用 absolute
來排列,需透過每個 element 的高度,幫每個 element 直接指定一個絕對位置,這樣就可以明確知道,當我滑動到某個距離時,該 render 哪些 element

需要透過以下資料,來做出 windowing 的效果:

已知

  • 每個 item 的高度
  • window 的高度
  • 目前的滑動距離

可以算出每個 item 相對於 Long List 的絕對距離,再根據目前的滑動距離計算出,目前的 window 要顯示的 item 範圍,就可以只 render window 內的 item。

使用者滾動之後,上述流程再跑一次。

簡單比較

Long List

  1. 抓資料
  2. render

Virtualized List

  1. 抓資料
  2. (需要的時候再)render

Infinite scrolling

  1. (需要的時候再)抓資料
  2. render

相關套件

其中官方建議的兩個套件 react-windowreact-virtualized,作者是同一個人(Brian Vaughn)。

兩者的區別只在於, 前者是後者的優化版,更快更輕量,基本上都用優化版即可,但因為 react-window 是針對常用的 element 架構(list & grid),因此若有比較特別的架構,還是要回歸到 react-virtualized

更詳細的差異可參考 [React-window 作者本人的說法](https://github.com/bvaughn/react-window#how-is-react-window-different-from-react-virtualized

Infinite scrolling 可以參考 react-window-infinite-loader

Virtualized List 的優缺點是什麼?

優點

大幅減少 render 時間,解決失幀問題,提升 UX

缺點

  • 通常需要調整既有的 HTML 結構
  • 子項目高度會變動的話,較難處理

Virtualized List 適合什麼情境?

針對資料量龐大,但無法切不同頁面處理時,可以用來處理 HTML 格式一致、高度盡量單純的 case,尤其需要把 RWD 的狀況考慮進來。

還有許多情境可以參考 React-window 作者簡報

結語

心智圖放大版

在公司中使用 React-window 時,其實踩了滿多坑的,剛開始套用時覺得很神奇,真的是大幅提升了 render 的速度。

但最大的坑在於,一定要確保子項目的高度在控制範圍內,比如說在 PC 版本每個子項目都寫死 60px 高度,但切換 RWD 到 mobile 版本時,可能文字太多就不小心換行,高度就被撐開了。

Virtualized List 因為是靠 absolute 定位高度,因此一定要能夠確保在任何情況下,掌控子項目高度不會暴走,否則整個 list 就會跑版很可怕。

參考資料

使用 react-window 虚拟化大型列表
今晚,我想來點 Web 前端效能優化大補帖!


上一篇
Day 12 - 為什麼要用 Day.js
下一篇
Day 14 - 為什麼要用 React Router
系列文
前端技能樹的十萬個為什麼30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言